<template>
    <div v-if="_current_record_id" class="summary-card">
        <div class="summary-card-title">{{ _current_model_cloud && _current_model_cloud.name }}</div>
        <div class="summary-card-subtitle">{{ _current_model && _current_model.model_eng_name }}</div>
        <div class="summay-tags">
            <el-tag class="local-model-tag" type="primary">{{ _current_model_cloud && _current_model_cloud.type }}</el-tag>
            <el-tag class="local-model-tag" type="primary">{{ _current_model_cloud && _current_model_cloud.subtype }}</el-tag>
            <el-tag class="local-model-tag" type="danger">{{ _current_model_cloud && _current_model_cloud.framework }}</el-tag>
        </div>
        <div class="summary-card-blocks">
            <div class="summary-card-block">
                <div class="summary-block-title">模型信息</div>
                <div class="summary-block-body">
                    <div class="summary-card-block-item">
                        <div class="summary-card-block-item-key">名称</div>
                        <div class="summary-card-block-item-value">
                            {{ _current_model && _current_model.model_eng_name }}
                        </div>
                    </div>
                    <div class="summary-card-block-item">
                        <div class="summary-card-block-item-key">框架</div>
                        <div class="summary-card-block-item-value">
                            {{ _current_model_cloud && _current_model_cloud.framework }}
                        </div>
                    </div>
                    <div class="summary-card-block-item">
                        <div class="summary-card-block-item-key">参数量</div>
                        <div class="summary-card-block-item-value">
                            {{ _current_model_cloud && _current_model_cloud.params_size }}
                        </div>
                    </div>
                </div>
            </div>
            <div class="summary-card-block">
                <div class="summary-block-title">数据集信息</div>
                <div class="summary-block-body">
                    <div class="summary-card-block-item">
                        <div class="summary-card-block-item-key">ID</div>
                        <div class="summary-card-block-item-value">
                            {{ _current_dataset && _current_dataset.id }}
                        </div>
                    </div>
                    <div class="summary-card-block-item">
                        <div class="summary-card-block-item-key">名称</div>
                        <div class="summary-card-block-item-value">
                            {{ _current_dataset && _current_dataset.dataset_name }}
                        </div>
                    </div>
                    <div class="summary-card-block-item">
                        <div class="summary-card-block-item-key">样本量</div>
                        <div class="summary-card-block-item-value">
                            {{ _current_dataset && _current_dataset.labeled_samples }}
                        </div>
                    </div>
                </div>
            </div>
            <div class="summary-card-block">
                <div class="summary-block-title">机器信息</div>
                <div class="summary-block-body">
                    <div class="summary-card-block-item">
                        <div class="summary-card-block-item-key">机器名</div>
                        <div class="summary-card-block-item-value">
                            {{ _current_model && _current_model.remote_id }}
                        </div>
                    </div>
                    <div class="summary-card-block-item">
                        <div class="summary-card-block-item-key">运行环境</div>
                        <div class="summary-card-block-item-value">
                            {{ _current_model && _current_model.env_name }}
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div v-if="_current_record_id" class="model-info-title">模型评测结果</div>
    <div v-if="_current_record_id" class="eval-chart-wrapper">
        <canvas id="canvas-eval" class="eval-chart"></canvas>
    </div>
    <el-table :data="_tableModels" border class="epoch-models-table" table-layout="fixed" highlight-current-row :header-cell-style="headerCellStyle">
        <el-table-column v-for="(column, index) in _columns" :key="column.prop" :prop="column.prop" :label="column.label">
            <template #header>
                <el-tooltip placement="top" :content="column.label">
                    <span class="custom-header">
                        {{ column.label }}
                    </span>
                </el-tooltip>
            </template>
        </el-table-column>
        <el-table-column v-if="_tableModels.length > 0" fixed="right" label="ops" width="150px">
            <template #default="scope">
                <div class="eval-ops">
                    <el-button link type="primary" size="small" @click.prevent="onClickModel(scope.$index)"> 导出模型 </el-button>
                    <div v-if="_evaling_models === scope.row.model_path" class="eval-loading-wrapper">
                        <Loading class="eval-loading-icon" />
                    </div>
                    <el-button v-if="_evaling_models !== scope.row.model_path" link type="primary" size="small" @click.prevent="onEvalDetails(scope.$index)">
                        {{ scope.row.exist_eval ? '评测详情' : '开始评测' }}
                    </el-button>
                </div>
            </template>
        </el-table-column>
    </el-table>
</template>

<script setup>
import API from '@/js/api';
import { ref, onMounted, nextTick } from 'vue';
const emit = defineEmits(['showEvalDetails']);
defineExpose({ clearSummary, setRecord, addScalars });
import { getFileBaseName } from '@/js/utils/fileUtils';

// 引入chart.js库，绘制相关图表
import { Chart, registerables } from 'chart.js';
Chart.register(...registerables);

// 基础变量(快捷键调用)
const name = 'ModelSummaryTab';

// 模型信息相关属性
let _current_record_id = ref(null);
let _current_evals_path = ref(null);

let _current_model = ref(null);
let _current_model_cloud = ref(null);
let _current_dataset = ref(null);

// 评测chart的相关属性
let _chart_ui = null;
let _chart_keys = null;
let _chart_data = {};

// 定义模型表格相关的属性
let _tableModels = ref([]);
let _columns = ref([]);
let _evaling_models = ref(null);

function headerCellStyle({ row, index }) {
    return {
        color: '#222',
        'background-color': '#efefef'
    };
}

async function onClickModel(index) {
    let model = _tableModels.value[index];
    console.log(`onClickModel model = `, model);
}

async function onEvalDetails(index) {
    let model = _tableModels.value[index];
    // console.log(`onEvalDetails _current_record_id = ${_current_record_id.value}, model = `, model);
    if (model.exist_eval) {
        // 模型存在评测结果，直接加载即可
        let result = await API.invoke('LoadEvalResults', {
            record_id: _current_record_id.value,
            model_path: model.model_path,
            filter: JSON.stringify({
                area: ['all', 'small', 'medium', 'large'],
                iou_threshold: ['0.5', '0.6', '0.7', '0.8', '0.9']
            })
        });
        if (result.code == 0) {
            emit('showEvalDetails', {
                record_id: _current_record_id.value,
                model_path: model.model_path,
                eval_details: result.msg
            });
        } else {
            _tableModels.value[index].exist_eval = false;
            ElMessage.error('获取评测结果失败，错误信息:' + result.msg);
        }
    } else {
        // 未找到评测结果，发起对于模型的评测
        let model_name = getFileBaseName(model.model_path);
        ElMessage.success(`模型[ ${model_name} ]开始评测...`);
        let result = await API.invoke(
            'EvalStart',
            {
                record_id: _current_record_id.value,
                model_path: model.model_path
            },
            onEvalCallback
        );
        console.log(`onEvalDetails result = `, result);
        if (result.code === 0) {
            _evaling_models.value = model.model_path;
        }
    }
}

async function onEvalCallback(task_id, message) {
    // console.log(`onEvalCallback task_id=${task_id}, message=`, message);
    try {
        let event = message.event;
        if (event == 'result') {
            let model_path = message.params.msg.model_path;
            let model_name = getFileBaseName(model_path);
            let row = _tableModels.value.find((item) => {
                return item.model_path === model_path;
            });
            _evaling_models.value = null;
            ElMessage.success(`模型[ ${model_name} ]评测完成!`);
            row.exist_eval = true;
        }
    } catch (error) {
        console.log('onEvalCallback error = ', error);
    }
}

// 清空摘要信息
async function clearSummary() {
    _current_record_id.value = null;
    await nextTick();

    // 清空模型基础信息
    _current_model.value = null;
    _current_model_cloud.value = null;
    _current_dataset.value = null;

    // 清空表格和信息
    _tableModels.value = [];
    _columns.value = [];
    _evaling_models.value = null;

    // 清空charts信息
    clearCharts();
}

// 设置record对象
async function setRecord(record, evalsPath) {
    _current_record_id.value = record.id;
    _current_evals_path.value = evalsPath;
    await nextTick();

    // 初始化评测展示的信息
    await initModelInfos(record.local_model_id);
    await initCharts(record.id);
    evalsPath && (await loadEvalData(evalsPath));
}

// 获取模型的信息
async function initModelInfos(local_model_id) {
    let result = await API.invoke('GetLocalModels', {});
    if (result.code === 0) {
        _current_model.value = result.msg.find((item) => item.id === local_model_id);
        // console.log('_current_model = ', _current_model.value);
    }

    result = await API.invoke('GetCloudModels', { model_id: _current_model.value.model_id });
    if (result.code === 0) {
        _current_model_cloud.value = result.msg[0];
        // console.log('_current_model_cloud = ', _current_model_cloud.value);
    }

    result = await API.invoke('GetDatasetInfo', { dataset_id: _current_model.value.dataset_id });
    if (result.code === 0) {
        _current_dataset.value = result.msg;
        // console.log('_current_dataset = ', _current_dataset.value);
    }
}

function addScalar(newData) {
    let has_labels = false;
    _chart_keys &&
        _chart_keys.forEach((label, index) => {
            if (newData[label] !== undefined) {
                has_labels = true;
                if (!_chart_data[label]) {
                    _chart_data[label] = [];
                }
                _chart_data[label].push(newData[label]);
                _chart_ui.data.datasets[index].data = _chart_data[label];
            }
        });
    if (has_labels) {
        _chart_ui.data.labels = Array.from({ length: _chart_data[_chart_keys[0]].length }, (_, i) => i + 1);
        _chart_ui.update('none');
    }
}

function addScalars(logData) {
    // console.log('SummaryPanel::addScalars logData = ', logData);
    let logDataEx = JSON.parse(logData);

    // 在chart里面插入数据
    addScalar(logDataEx);

    // 在表格里插入数据
    if (_chart_keys.length > 0 && logDataEx.hasOwnProperty(_chart_keys[0])) {
        addTableData(logDataEx);
    }
}

function addTableData(tableData) {
    // console.log('addTableData tableData = ', tableData);
    if (_columns.value.length == 0) {
        let excludeKeys = ['data_time', 'time'];
        let cols = Object.keys(tableData).filter((key) => {
            return !excludeKeys.includes(key);
        });

        // 如果cols中有'step'，将其置于第一位
        if (cols.includes('step')) {
            cols = ['step', ...cols.filter((col) => col !== 'step')];
        }
        _columns.value = cols.map((col, index) => {
            return {
                prop: col,
                label: col
            };
        });
    }

    _tableModels.value.push(tableData);
}

function addTableDatas(tableDatas) {
    let blacklist = ['model_path', 'exist_eval'];
    if (_columns.value.length == 0) {
        let cols = Object.keys(tableDatas[0]).filter((key) => {
            return !blacklist.includes(key);
        });
        _columns.value = cols.map((col, index) => {
            return {
                prop: col,
                label: col
            };
        });
        // console.log(_columns.value);
    }
    _tableModels.value = tableDatas;
}

async function loadEvalData(evalsPath) {
    let fileContent = await API.invoke('ReadFile', { file_path: evalsPath });
    let evals = JSON.parse(fileContent.msg);
    if (evals.length > 0) {
        // 在表格中插入数据
        addTableDatas(evals);

        // 在chart中插入数据
        evals.forEach((newData) => {
            addScalar(newData);
        });
    }
}

async function clearCharts() {
    _chart_ui && (await _chart_ui.destroy());
    _chart_ui = null;
    _chart_keys = null;
    _chart_data = {};
}

async function initCharts(record_id) {
    // 清空对应的图表
    await clearCharts();

    // 读取配置信息
    let result = await API.invoke('GetRecordVisConfig', {
        record_id: record_id
    });
    // console.log('GetRecordVisConfig result = ', result);
    if (result.code === 0) {
        let visConfig = result.msg;
        // 初始化图表控件
        _chart_keys = visConfig.evaluation.keys;
        _chart_ui = initChart('canvas-eval', _chart_keys, '评测曲线');
    }
}

function initChart(canvas_id, ylabels, titles) {
    const canvas = document.getElementById(canvas_id);
    canvas.style.width = '550px';
    canvas.style.height = '280px';
    const ctx = canvas.getContext('2d');
    let datasets = ylabels.map((label) => {
        return {
            label: label,
            data: [],
            borderWidth: 2,
            tension: 0
        };
    });
    let chart = new Chart(ctx, {
        type: 'line',
        data: {
            labels: [],
            datasets: datasets
        },
        options: {
            animation: false,
            scales: {
                x: { title: { display: true, text: titles.x } },
                y: { title: { display: true, text: titles.y } }
            },
            plugins: {
                legend: { display: true }
            },
            elements: {
                point: { radius: 1 }
            },
            responsive: false
        }
    });
    return chart;
}

onMounted(() => {});
</script>

<style scoped>
.epoch-models-table {
    width: 100%;
    padding: 5px;
}

.custom-header {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.summary-card {
    width: 100%;
    box-sizing: border-box;
}

.summary-card-title {
    font-size: 22px;
    font-weight: 800px;
    padding: 3px;
    margin: 8px 3px 0px 3px;
    /* text-align: center; */
}

.summary-card-subtitle {
    font-size: 18px;
    font-weight: 800px;
    padding: 0px;
    margin: 0px 3px 20px 8px;
    color: #6d6d6d;
}

.summay-tags {
    margin-left: 8px;
}

.summary-card-blocks {
    display: flex;
    width: 100%;
    padding: 5px;
    box-sizing: border-box;
    margin-top: 5px;
}

.summary-card-block {
    width: 33%;
    box-shadow: 2px 2px rgba(193, 193, 241, 0.5);
    background-color: #e9e9e9;
    margin: 3px;
    padding: 5px;
}

.summary-block-title {
    font-size: 16px;
    font-weight: 600;
    text-align: center;
    margin-bottom: 5px;
    color: #3c3c3c;
}

.summary-block-body {
    background-color: white;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    padding: 10px 5px 5px 5px;
    box-shadow: inset 3px 3px rgb(223, 223, 223);
    height: calc(100% - 45px);
}

.summary-card-block-item {
    display: flex;
    justify-content: space-between;
}

.summary-card-block-item-key {
    font-size: 14px;
    margin-left: 3px;
    min-width: 50px;
    color: #5c5c5c;
}

.summary-card-block-item-value {
    font-size: 14px;
    margin-right: 3px;
    color: #3a3a3a;
}

.local-model-tag {
    margin-right: 8px;
}

.model-info-title {
    font-size: 20px;
    margin: 10px 5px;
}

.eval-chart-wrapper {
    display: flex;
    justify-content: center;
    align-items: center;
    width: calc(100% - 8px);
    height: 300px;
    padding: 5px 50px 5px 30px;
    margin: 5px 10px 10px 2px;
    box-shadow: 2px 2px rgba(193, 193, 241, 0.5);
    background-color: rgb(215, 215, 215);
    box-sizing: border-box;
}

.eval-chart {
    background-color: white;
}

.eval-ops {
    display: flex;
    box-sizing: border-box;
}

.eval-loading-wrapper {
    padding: 1px 30px;
    box-sizing: border-box;
    height: 20px;
}

.eval-loading-icon {
    width: 16px;
    height: 16px;
    animation: spin 2s linear infinite;
}

@keyframes spin {
    0% {
        transform: rotate(0deg);
    }

    100% {
        transform: rotate(360deg);
    }
}
</style>
